﻿using System;
using System.Collections.Generic;
using System.Linq;
using Commands.Scheduling;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestProject
{
    [TestClass]
    public class ScheduleTest
    {
        [TestMethod]
        public void _01_BeforeFirstTime()
        {
            var times = _GetTimesSorted();

            var wakeUpTime = _GetTime(times, _AddTimeSpan(times, 0, TimeSpan.FromMinutes(-30))) ;

            Assert.AreEqual(times.First(), wakeUpTime.TimeOfDay);
        }

        [TestMethod]
        public void _02_SameAsFirstTime()
        {
            var times = _GetTimesSorted();

            var suspendTime = _AddTimeSpan(times, 0, TimeSpan.FromMinutes(0));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(times[1], wakeUpTime.TimeOfDay);
        }

        [TestMethod]
        public void _03_BeforeSecondTime()
        {
            var times = _GetTimesSorted();

            var suspendTime = _AddTimeSpan(times, 0, TimeSpan.FromMinutes(1));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(times[1], wakeUpTime.TimeOfDay);
        }

        [TestMethod]
        public void _04_BeforeThirdTime()
        {
            var times = _GetTimesSorted();

            var suspendTime = _AddTimeSpan(times, 1, TimeSpan.FromMinutes(28));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(times[2], wakeUpTime.TimeOfDay);
        }

        [TestMethod]
        public void _04_BeforeFourthTime()
        {
            var times = _GetTimesSorted();

            var suspendTime = _AddTimeSpan(times, 2, TimeSpan.FromMinutes(115));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(times[3], wakeUpTime.TimeOfDay);
        }

        [TestMethod]
        public void _05_AfterLastTime()
        {
            var times = _GetTimesSorted();

            var suspendTime = _AddTimeSpan(times, 3, TimeSpan.FromMinutes(1));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(_AddTimeSpan(times, 0, TimeSpan.FromDays(1)), wakeUpTime);
        }

        [TestMethod]
        public void _06_UnsortedBeforeFourthTime()
        {
            var times = _GetTimesUnSorted();

            var suspendTime = _AddTimeSpan(times, 0, TimeSpan.FromMinutes(1));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(times[3], wakeUpTime.TimeOfDay);
        }
        
        [TestMethod]
        public void _07_MidnightNextDay()
        {
            var times = _GetTimesWithMidnightSorted();

            var suspendTime = _AddTimeSpan(times, 3, TimeSpan.FromMinutes(11));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(_AddTimeSpan(times, 4, TimeSpan.Zero), wakeUpTime);
        }

        [TestMethod]
        public void _071_MidnightNextDayUnsorted()
        {
            var times = _GetTimesWithMidnightUnSorted();

            var suspendTime = _AddTimeSpan(times, 3, TimeSpan.FromMinutes(11));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(_AddTimeSpan(times, 0, TimeSpan.Zero), wakeUpTime);
        }

        [TestMethod]
        public void _072_MidnightThisDayUnsorted()
        {
            var times = _GetTimesWithMidnightThisDayUnSorted();

            var suspendTime = _AddTimeSpan(times, 3, TimeSpan.FromMinutes(11));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(_AddTimeSpan(times, 2, TimeSpan.FromDays(1)), wakeUpTime);
        }
        
        [TestMethod]
        public void _08_HasNextDayValues()
        {
            var times = _GetTimesSorted();
            var nextMidnight = TimeSpan.FromDays(1);
            times.Add(nextMidnight.Add(TimeSpan.FromTicks(1)));

            // Get time after last ts.
            var suspendTime = _AddTimeSpan(times, 3, TimeSpan.FromMinutes(11));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(_AddTimeSpan(times, 0, TimeSpan.FromDays(1)), wakeUpTime);
        }

        [TestMethod]
        public void _09_HasNextDayValuesUnsorted()
        {
            var times = _GetTimesUnSorted();
            var nextMidnight = TimeSpan.FromDays(1);
            times.Add(nextMidnight.Add(TimeSpan.FromTicks(1)));

            times.Add(TimeSpan.FromDays(2));

            // Get time after last ts.
            var suspendTime = _AddTimeSpan(times, 2, TimeSpan.FromMinutes(11));
            var wakeUpTime = _GetTime(times, suspendTime);

            Assert.AreEqual(_AddTimeSpan(times, 1, TimeSpan.FromDays(1)), wakeUpTime);
        }

        #region Helpers


        private DateTime _AddTimeSpan(IList<TimeSpan> times, int index, TimeSpan ts)
        {
            return DateTime.Now.Date.Add(times[index].Add(ts));
        }

        private IList<TimeSpan> _GetTimesWithMidnightSorted()
        {
            var times = new List<TimeSpan>();
            times.Add(new TimeSpan(8, 0, 0));
            times.Add(new TimeSpan(12, 0, 0));
            times.Add(new TimeSpan(15, 0, 0));
            times.Add(new TimeSpan(20, 0, 0));
            times.Add(new TimeSpan(24, 0, 0));

            return times;
        }

        private IList<TimeSpan> _GetTimesWithMidnightUnSorted()
        {
            var times = new List<TimeSpan>();
            times.Add(new TimeSpan(24, 0, 0));
            times.Add(new TimeSpan(12, 0, 0));
            times.Add(new TimeSpan(8, 0, 0));
            times.Add(new TimeSpan(20, 0, 0));
            times.Add(new TimeSpan(15, 0, 0));

            return times;
        }

        private IList<TimeSpan> _GetTimesWithMidnightThisDayUnSorted()
        {
            var times = new List<TimeSpan>();
            times.Add(new TimeSpan(12, 0, 0));
            times.Add(new TimeSpan(8, 0, 0));
            times.Add(new TimeSpan(0, 0, 0));
            times.Add(new TimeSpan(20, 0, 0));
            times.Add(new TimeSpan(15, 0, 0));
            times.Add(new TimeSpan(24, 0, 0));

            return times;
        }

        private IList<TimeSpan> _GetTimesUnSorted()
        {
            var times = new List<TimeSpan>();
            times.Add(new TimeSpan(12, 0, 0));
            times.Add(new TimeSpan(8, 0, 0));
            times.Add(new TimeSpan(20, 0, 0));
            times.Add(new TimeSpan(15, 0, 0));

            return times;
        }

        private IList<TimeSpan> _GetTimesSorted()
        {
            var times = new List<TimeSpan>();
            times.Add(new TimeSpan(8, 0, 0));
            times.Add(new TimeSpan(12, 0, 0));
            times.Add(new TimeSpan(15, 0, 0));
            times.Add(new TimeSpan(20, 0, 0));

            return times;
        }

        private DateTime _GetTime(IEnumerable<TimeSpan> times, DateTime time)
        {
            var schedule = _CreateSchedule(times);

            return schedule.GetWakeupTime(time);
        }

        private Schedule _CreateSchedule(IEnumerable<TimeSpan> times)
        {
            return new Schedule(true, false, TimeSpan.FromMinutes(1),
                 false, false, TimeSpan.FromMinutes(1), times);
        }
            
        #endregion
    }
}
